/// 监视器信息输出实现
pub trait MonitorPrinterImpl {
    /// 监视器打印输出
    fn print(&self, msg: String);

    /// 监视器终端字符列数
    fn cols(&self) -> usize;

    /// 是否是主监视器
    fn is_main_mon(&self) -> bool {
        false
    }
}

/// 监视器信息输出
///
/// 命令不直接使用 println/print 输出信息, 而是使用 `MonitorPrinter`,
/// 便于输出统一重定向位置.
pub struct MonitorPrinter {
    printer: Box<dyn MonitorPrinterImpl + Sync + Send>,
}

impl MonitorPrinter {
    /// 创建一个监视器打印者
    pub fn create(printer: Box<dyn MonitorPrinterImpl + Sync + Send>) -> Self {
        MonitorPrinter { printer }
    }

    /// 将 readline 输出信息写入监视器
    pub fn print(&self, msg: String) {
        self.printer.print(msg);
    }

    /// 是否是主监视器
    ///
    /// 对于打印者实现, 目前实现的 readline 功能会注册原生 Printer 到
    /// reedline 内部, 因此当依赖 reedline 实现的命令输出可能与预期位置不同,
    /// 比如 histroy: 该实现依赖 reedline 内部实现, 因此如果在使用 `char_mon`
    /// 实现的命令行交互时 history 的输出不同输出到字符设备中,
    /// 而是输出到原生的标准输入输出中, 因此使用该函数判断当前打印者是否是 reedline,
    /// 命令可以通过该函数执行额外的处理来避免错误发生.
    pub fn is_main_mon(&self) -> bool {
        self.printer.is_main_mon()
    }

    /// 格式化元素
    ///
    /// 根据终端宽度以及输入元素个数对齐格式化返回字符串
    ///
    /// # Panics
    /// 当传入的 `v` 为空时, 将会 panic
    pub fn print_term(&self, v: &[String]) {
        let min_col_pad = 2;
        let cols = self.printer.cols();
        let max_width = std::cmp::min(cols, v.iter().map(|s| s.len()).max().unwrap() + min_col_pad);
        let num_cols = cols / max_width;
        let num_rows = (v.len() + num_cols - 1) / num_cols;

        for row in 0..num_rows {
            let mut ab = String::new();

            for col in 0..num_cols {
                let i = (col * num_rows) + row;
                if i < v.len() {
                    let ca = &v[i];
                    let width = ca.len();
                    ab.push_str(ca);
                    if ((col + 1) * num_rows) + row < v.len() {
                        for _ in width..max_width {
                            ab.push(' ');
                        }
                    }
                }
            }
            self.print(ab);
        }
    }
}
